// Copyright 2004 The Apache Software Foundation
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain token copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace AspectSharp.Core.Proxy
{
    using System;
    using AspectSharp.Core.Dispatcher;
    using AspectSharp.Lang.AST;

	/// <summary>
	/// Default implementation of IProxyFactory which uses Castle.DynamicProxy to
	/// generate the proxies.
	/// </summary>
	public class DefaultProxyFactory : IProxyFactory
	{
		private readonly CustomProxyGenerator   _generator ;

        private IInterceptorFactory _interceptorFactory;
		
		private AspectEngine _engine;
		
		/// <summary>
		/// Constructs token DefaultProxyFactory
		/// </summary>
		/// <param name="engine"></param>
		/// <param name="dispatcherFactory"></param>
		public DefaultProxyFactory(AspectEngine engine, IInterceptorFactory interceptorFactory)
		{
			AssertUtil.ArgumentNotNull( engine, "engine" );
			AssertUtil.ArgumentNotNull( interceptorFactory, "dispatcherFactory" );
			
			_engine = engine;
			_interceptorFactory = interceptorFactory;
            _generator = new CustomProxyGenerator(new Castle.DynamicProxy.ProxyGenerator());
		}

		/// <summary>
		/// Constructs token DefaultProxyFactory
		/// </summary>
		/// <param name="engine"></param>
		public DefaultProxyFactory(AspectEngine engine) : this( engine, new DefaultInterceptorFactory() )
		{
		}

		#region IProxyFactory Members

		public object CreateClassProxy(Type classType, AspectDefinition aspect)
		{
			AssertUtil.ArgumentNotNull(classType, "classType");
			AssertUtil.ArgumentNotNull(aspect, "aspect");

			IAspectInterceptor interceptor = _interceptorFactory.Create(aspect, _engine);

			return CreateAndInstantiateClassProxy(classType, aspect, interceptor);
		}

       

        public object CreateInterfaceProxyWithTarget(Type inter, object target, AspectDefinition aspect)
		{
			AssertUtil.ArgumentNotNull(inter, "inter");
			AssertUtil.ArgumentNotNull(target, "target");
			AssertUtil.ArgumentNotNull(aspect, "aspect");

			IAspectInterceptor dispatcher = _interceptorFactory.Create(aspect, _engine);

			return CreateAndInstantiateInterfaceProxy(inter, target, aspect, dispatcher);
		}

		#endregion

		protected virtual object CreateAndInstantiateClassProxy(Type baseClass, AspectDefinition aspect, IAspectInterceptor dispatcher)
		{
			object proxy = null;

			object[] mixins = InstantiateMixins( aspect.Mixins );
            proxy = _generator.CreateClassProxy(baseClass, mixins, dispatcher);
			InitializeMixins( proxy, mixins );

			return proxy;
		}

		protected virtual object CreateAndInstantiateInterfaceProxy(Type inter, object target, AspectDefinition aspect, IAspectInterceptor dispatcher)
		{
			object proxy = null;

			object[] mixins = InstantiateMixins( aspect.Mixins );
            proxy = _generator.CreateInterfaceProxyWithTarget(inter, target, mixins, dispatcher);
			InitializeMixins( proxy, mixins );

			return proxy;
		}
              

		#region Mixin related methods

		protected void InitializeMixins(object proxy, object[] mixins)
		{
			for(int i=0; i < mixins.Length; i++)
			{
				object mixin = mixins[i];
				
				if (mixin is IProxyAware)
				{
					(mixin as IProxyAware).SetProxy(proxy);
				}
			}
		}

		protected object[] InstantiateMixins(MixinDefinitionCollection mixins)
		{
			object[] instances = new object[ mixins.Count ];

			for(int i=0; i < mixins.Count; i++)
			{
				MixinDefinition definition = mixins[i];
				Type mixinType = definition.TypeReference.ResolvedType;
				
				try
				{
					instances[i] = Activator.CreateInstance( mixinType );
				}
				catch(Exception e)
				{
					throw new ProxyFactoryException("Could not instantiate mixin " + mixinType.FullName, e);
				}
			}

			return instances;
		}

		#endregion
	}
}
